package com.seu.githubcn.controller;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.seu.githubcn.config.Result;
import com.seu.githubcn.entity.Manager;
import com.seu.githubcn.entity.Repo;
import com.seu.githubcn.entity.User;
import com.seu.githubcn.entity.UserAction;
import com.seu.githubcn.mapper.ManagerMapper;
import com.seu.githubcn.mapper.RepoMapper;
import com.seu.githubcn.mapper.UserActionMapper;
import com.seu.githubcn.mapper.UserMapper;
import org.eclipse.jgit.api.Git;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.beans.factory.annotation.Qualifier;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.seu.githubcn.entity.UserAction.ActionName.FORK;
import static com.seu.githubcn.entity.UserAction.ActionName.STAR;
import static com.seu.githubcn.utils.Utils.*;

@RestController
@RequestMapping()
public class RepoController {

    @Autowired
    RepoMapper repoMapper;

    @Autowired
    UserMapper userMapper;

    @Autowired
    ManagerMapper managerMapper;

    @Autowired
    UserActionMapper userActionMapper;


    @Value("${githubcn.base-path}")
    String basePath;


    /**
     * 返回用户所有管理的仓库
     * @param userId id
     * @return 200
     */
    @GetMapping("/repo/list")
    public Result<?> getRepoList(@RequestParam String userId){
        Integer id = Integer.parseInt(userId);
        List<Manager> managerList =
                managerMapper.selectList(Wrappers.<Manager>lambdaQuery()
                        .eq(Manager::getManagerId,id));

        var resInfo = new ArrayList<>();
        //TODO
        //现在manager有仓库名了
        for(var manager:managerList){
            List<Repo> repoList =
                    repoMapper.selectList(Wrappers.<Repo>lambdaQuery()
                    .eq(Repo::getRepoId,manager.getRepoId()));
            resInfo.addAll(repoList);
        }
        return new Result<>(200, resInfo, "查询成功！");
    }



    /**
     * 新建仓库
     * @param repo 必须要有ownerId和repoName
     * @return 创建消息
     */
    @PostMapping("/newRepo")
    public Result<?> newRepo(@RequestBody Repo repo) {
        User owner = userMapper.selectById(repo.getOwnerId());
        if (owner == null || repo.getRepoName() == null || repo.getDefaultBranch() == null) {
            return new Result<>(402, null, "创建失败！");
        }
        Repo ifExist = repoMapper.selectOne(Wrappers.<Repo>lambdaQuery()
                .eq(Repo::getOwnerId, owner.getUserId())
                .eq(Repo::getRepoName,repo.getRepoName()));
        if (ifExist != null) {
            return new Result<>(402, null, "已有重名仓库！");
        }

        var localPath = basePath.formatted(owner.getUserName(), repo.getRepoName());

        try {
            File localFile = new File(localPath+".git");
            Git.init().setBare(true).setDirectory(localFile).call();
            Git clone = fromCloneRepository(localFile.getAbsolutePath(),
                    "/tmp%s".formatted(localPath),null);
            File readme = new File("/tmp%s/%s".formatted(localPath,"README.md"));
            readme.createNewFile();
            clone.add().addFilepattern(".").call();
            clone.commit().setMessage("initial commit").call();
            clone.push().call();
            clone.branchCreate().setName("ignore").call();
            clone.checkout().setName("ignore").call();

        } catch (Exception e) {
            return new Result<>(402, null, "创建失败！");
        }

        Repo newRepo = new Repo();
        newRepo.setRepoName(repo.getRepoName());
        newRepo.setOwnerId(owner.getUserId());
        newRepo.setOwnerName(owner.getUserName());
        if(repo.getDescription()==null||repo.getDescription().equals("")){
            newRepo.setDescription("该用户太懒了，还没有做仓库介绍-_-");
        }else {
            newRepo.setDescription(repo.getDescription());
        }
        if(repo.getDefaultBranch() == null || repo.getDefaultBranch().equals("")){
            newRepo.setDefaultBranch(repo.getDefaultBranch());
        }else{
            newRepo.setDefaultBranch("master");
        }
        if(repo.getIsPrivate() == null){
            newRepo.setIsPrivate(true);
        }else{
            newRepo.setIsPrivate(repo.getIsPrivate());
        }
        newRepo.setIsForked(false);
        repoMapper.insert(newRepo);
        Repo _repo =
                repoMapper.selectOne(Wrappers.<Repo>lambdaQuery()
                        .eq(Repo::getRepoName,newRepo.getRepoName())
                        .eq(Repo::getOwnerId,owner.getUserId()));
        Manager manager = new Manager();
        manager.setManagerId(owner.getUserId());

        manager.setRepoId(_repo.getRepoId());

        managerMapper.insert(manager);
        //仓库拥有者也是管理员

        return new Result<>(200, null, "创建成功！");
    }


    /**
     * 删除仓库
     * @param repo 仓库信息
     * @return 200 or 402
     */
    @PostMapping("/repo/delete")
    public Result<?> deleteRepo(@RequestBody Repo repo){
        Repo repoFromDB =
                repoMapper.selectOne(Wrappers.<Repo>lambdaQuery()
                .eq(Repo::getRepoName, repo.getRepoName())
                .eq(Repo::getOwnerId, repo.getOwnerId()));
        if(repoFromDB == null){
            return new Result<>(402, null, "不存在该仓库！");
        }

        User user = userMapper.selectById(repoFromDB.getOwnerId());

        var localPath = basePath.formatted(
                user.getUserName(), repoFromDB.getRepoName()+".git");

        if(!deleteByPath(localPath)){
            return new Result<>(402, null, "删除失败！");
        }

        repoMapper.deleteById(repoFromDB.getRepoId());
        for ( var one : managerMapper.selectList(Wrappers.<Manager>lambdaQuery()
                .eq(Manager::getRepoId, repo.getRepoId()))){
            managerMapper.deleteById(one.getId());
        }
        if(repoFromDB.getIsForked()){
            userActionMapper.delete(Wrappers.<UserAction>lambdaQuery()
                    .eq(UserAction::getUserId,user.getUserId())
                    .eq(UserAction::getRepoId,repoFromDB.getRepoId()));
        }
        return new Result<>(200, null, "删除成功！");

    }


    /**
     * 修改仓库信息
     * @param repo 仓库信息
     */
    @PostMapping("/repo/renew/info")
    public Result<?> renewRepo(@RequestBody Repo repo){
        Repo recent =
                repoMapper.selectOne(Wrappers.<Repo>lambdaQuery()
                        .eq(Repo::getRepoName, repo.getRepoName()).eq(Repo::getOwnerName,repo.getOwnerId())
                );
        recent.setDescription(repo.getDescription());
        recent.setIsPrivate(repo.getIsPrivate());
        repoMapper.updateById(recent);
        return new Result<>(200,null,"修改成功");
    }

    /**
     * 获取仓库信息
     * @param repo 仓库信息
     */
    @PostMapping("/repo/info")
    public Result<?> getRepo(@RequestBody Repo repo){
        Repo recent =
                repoMapper.selectOne(Wrappers.<Repo>lambdaQuery()
                        .eq(Repo::getRepoName, repo.getRepoName()).eq(Repo::getOwnerName,repo.getOwnerId())
                );
        return new Result<>(200,recent,"仓库信息查询成功");
    }


    /**
     * 全局的搜索仓库，只能搜索public仓库
     * @param currentPage 当前页
     * @param pageSize 页条数
     * @param search 搜索关键字
     * @return 200
     */
    @GetMapping("/repo/search")
    public Result<?> publicSearch(@RequestParam(defaultValue = "1") Integer currentPage,
                                  @RequestParam(defaultValue = "10") Integer pageSize,
                                  @RequestParam(defaultValue = "0") String search){

        Page<Repo> searchPage =
                repoMapper.selectPage(new Page<>(currentPage,pageSize),
                        Wrappers.<Repo>lambdaQuery()
                                .eq(Repo::getIsPrivate,false)
                                .and(Wrappers->Wrappers
                                        .like(Repo::getRepoName,search)
                                        .or()
                                        .like(Repo::getDescription,search))
                                );

        return new Result<>(200,searchPage,"查询成功!");
    }


    /**
     * 搜索 当前用户的仓库
     * @param searchBody 搜索关键字
     * @return 200
     */
    @PostMapping("/repo/local/search")
    public Result<?> localSearch(@RequestBody User user,String searchBody){

        var searcher =
                userMapper.selectById(user.getUserId());
        var localRepoList =
                repoMapper.selectList(Wrappers.<Repo>lambdaQuery()
                        .eq(Repo::getOwnerId,searcher.getUserId())
                        .and(Wrappers->Wrappers
                                .like(Repo::getRepoName,searchBody)
                                .or()
                                .like(Repo::getDescription,searchBody))
                );


        return new Result<>(200,localRepoList,"查询成功!");
    }

    @PostMapping("/repo/fork")
    public Result<?> fork(@RequestBody Map<String,String>map){
        var _userId = map.get("userId");
        var userName = map.get("userName");
        var userId = Integer.parseInt(_userId);
        var ownerName = map.get("ownerName");
        var repoName = map.get("repoName");
        List<Repo> repoList =
                repoMapper.selectList(Wrappers.<Repo>lambdaQuery()
                        .eq(Repo::getOwnerId,userId));
        for(var one : repoList){
            if(one.getRepoName().equals(repoName)){
                return new Result<>(400,null,"有重名仓库");
            }
        }
        var base = "file:///D:\\github_cn\\%s\\%s.git".formatted(ownerName, repoName);
        var fork = basePath.formatted(userName,repoName)+".git";
        forkRepository(base,fork,null);
        fromCloneRepository(base,"/tmp"+fork,null);
        Repo repo = new Repo();
        repo.setRepoName(repoName);
        repo.setIsForked(true);
        repo.setIsPrivate(false);
        repo.setOwnerId(userId);
        repo.setOwnerName(userName);
        repo.setDescription("fork 仓库");
        repo.setDefaultBranch("master");
        repoMapper.insert(repo);
        var forkRepo =
                repoMapper.selectOne(Wrappers.<Repo>lambdaQuery()
                .eq(Repo::getOwnerName,ownerName)
                .eq(Repo::getRepoName,repoName));
        UserAction userAction = new UserAction();
        userAction.setUserId(userId);
        userAction.setRepoId(forkRepo.getRepoId());
        userAction.setActionName(UserAction.ActionName.FORK);
        userActionMapper.insert(userAction);
        Repo repoFromDB =
                repoMapper.selectOne(Wrappers.<Repo>lambdaQuery()
                        .eq(Repo::getRepoName, repo.getRepoName())
                        .eq(Repo::getOwnerId, repo.getOwnerId()));
        Manager manager = new Manager();
        manager.setManagerId(userId);
        manager.setRepoId(repoFromDB.getRepoId());
        managerMapper.insert(manager);
        //仓库拥有者也是管理员
        return new Result<>(200,null,"fork成功");
    }

    @PostMapping("repo/star")
    public Result<?> star(@RequestBody Map<String,String>map){
        var _userId = map.get("userId");
        var userId = Integer.parseInt(_userId);
        var ownerName = map.get("ownerName");
        var repoName = map.get("repoName");
        var repo =
                repoMapper.selectOne(Wrappers.<Repo>lambdaQuery()
                        .eq(Repo::getOwnerName,ownerName)
                        .eq(Repo::getRepoName,repoName));
        var ifStarred =
                userActionMapper.selectOne(Wrappers.<UserAction>lambdaQuery()
                        .eq(UserAction::getUserId,userId)
                        .eq(UserAction::getRepoId,repo.getRepoId())
                        .eq(UserAction::getActionName,STAR));
        if(ifStarred!=null){
            return new Result<>(400,null,"已经star过了！");
        }
        UserAction userAction = new UserAction();
        userAction.setUserId(userId);
        userAction.setRepoId(repo.getRepoId());
        userAction.setActionName(STAR);
        userActionMapper.insert(userAction);

        return new Result<>(200,null,"star成功！");
    }

    @PostMapping("repo/cancel_star")
    public Result<?> cancelStar(@RequestBody Map<String,String>map){
        var _userId = map.get("userId");
        var userId = Integer.parseInt(_userId);
        var ownerName = map.get("ownerName");
        var repoName = map.get("repoName");
        var repo =
                repoMapper.selectOne(Wrappers.<Repo>lambdaQuery()
                        .eq(Repo::getOwnerName,ownerName)
                        .eq(Repo::getRepoName,repoName));
        var ifStarred =
                userActionMapper.selectOne(Wrappers.<UserAction>lambdaQuery()
                        .eq(UserAction::getUserId,userId)
                        .eq(UserAction::getRepoId,repo.getRepoId())
                        .eq(UserAction::getActionName,STAR));
        if(ifStarred==null){
            return new Result<>(400,null,"尚未star！");
        }
        userActionMapper.delete(Wrappers.<UserAction>lambdaQuery()
                .eq(UserAction::getRepoId,repo.getRepoId())
                .eq(UserAction::getUserId,userId)
                .eq(UserAction::getActionName,STAR));

        return new Result<>(200,null,"取消star成功！");
    }

    @GetMapping("/{userName}/{repoName}/count")
    public Result<?> count(@PathVariable("userName") String userName,
                           @PathVariable("repoName") String repoName,
                           @RequestParam("userId") String userId){
        Integer id = Integer.parseInt(userId);
        var repo =
                repoMapper.selectOne(Wrappers.<Repo>lambdaQuery()
                        .eq(Repo::getOwnerName,userName)
                        .eq(Repo::getRepoName,repoName));
        var starCount =
                userActionMapper.selectCount(Wrappers.<UserAction>lambdaQuery()
                        .eq(UserAction::getRepoId,repo.getRepoId())
                        .eq(UserAction::getActionName,STAR));

        var forkCount =
                userActionMapper.selectCount(Wrappers.<UserAction>lambdaQuery()
                        .eq(UserAction::getRepoId,repo.getRepoId())
                        .eq(UserAction::getActionName,FORK));
        var isStar =
                userActionMapper.selectOne(Wrappers.<UserAction>lambdaQuery()
                        .eq(UserAction::getUserId,id)
                        .eq(UserAction::getRepoId,repo.getRepoId())
                        .eq(UserAction::getActionName,STAR));
        var isFork =
                userActionMapper.selectOne(Wrappers.<UserAction>lambdaQuery()
                        .eq(UserAction::getUserId,id)
                        .eq(UserAction::getRepoId,repo.getRepoId())
                        .eq(UserAction::getActionName,FORK));
        Map<String,Object> map = new HashMap<>();
        map.put("star",starCount);
        map.put("fork",forkCount);
        if(isStar==null){
            map.put("isStar",false);
        }else{
            map.put("isStar",true);
        }
        if(isFork==null){
            map.put("isFork",false);
        }else{
            map.put("isFork",true);
        }
        return new Result<>(200,map,"查询成功！");
    }


}
